package xueqiu

import (
	"encoding/json"
	"fmt"
	"gitee.com/gcom/gbox/errors"
	"gitee.com/gcom/stockdock/comm"
	"gitee.com/gcom/stockdock/core/support"
	"gitee.com/gcom/stockdock/crawler"
	"time"
)

func GetStockQuote0(tctx support.TracerCtx, codes []string) ([]*StockQuote, error) {
	if len(codes) == 0 {
		return nil, nil
	}

	var count, total = 0, len(codes)
	var result []*StockQuote
	var codeCh = make(chan string)
	var sem = make(chan int)
	defer close(codeCh)
	onValue := func(req crawler.CrawlRequest, stock *StockQuote) {
		count += 1
		tctx.Logger.Infof("[%d/%d]已获取股票(%s:%s)报价", count, total, stock.Code, stock.Name)
		result = append(result, stock)
		sem <- 1 // produce one ticket
	}
	onError := func(req crawler.CrawlRequest, err error) {
		tctx.Logger.Errorf("获取股票历史报价失败, %e", err)
		close(sem)
	}

	go func() {
		GetStockQuote(tctx, codeCh, onValue, onError)
	}()

	for i, v := range codes {
		codeCh <- v

		select {
		case _, ok := <-sem: // consume 1 ticket
			if !ok {
				tctx.Logger.Errorf("异常退出，共获取报价股票%d支", count)
				return result, errors.New(fmt.Sprintf("异常退出，共获取报价股票%d支", count)) // task aborted
			}
		}

		if i == total-1 {
			close(sem)
			tctx.Logger.Infof("共获取报价股票%d支", count)
			break
		}

		time.Sleep(1 * time.Second)
	}
	return result, nil
}

// GetStockQuote 获取雪球股票日报价
func GetStockQuote(tctx support.TracerCtx, codes <-chan string, onValue func(crawler.CrawlRequest, *StockQuote), onError crawler.CrawlOnError) {
	cctx := crawler.NewCrawlContext(tctx)
	defer cctx.Cancel()
	url := "https://xueqiu.com/S/SH000001"
	request := make(chan crawler.CrawlRequest)
	defer close(request)
	onValueFunc := func(r crawler.CrawlRequest, val string) {
		stockRes := struct {
			Code int    `json:"error_code"`        // 错误码，0表示正常
			Msg  string `json:"error_description"` // 错误消息
			Data struct {
				Quote *StockQuote `json:"quote"`
			} `json:"data"`
		}{}
		code := r.(*StockQuoteCrawlRequest).Code
		defer func() {
			if rec := recover(); rec != nil {
				e := errors.Newf("解析雪球股票(%s)报价数据失败, <%s>\n%v", code, val, rec)
				tctx.Logger.Errorf("解析雪球股票(%s)报价数据失败, <%s>\n%v", code, val, e)
				onError(r, e)
			}
		}()
		if err := json.Unmarshal([]byte(val), &stockRes); err != nil {
			tctx.Logger.Errorf("解析雪球股票(%s)报价数据失败, <%s>\n%v", code, val, err)
			onError(r, err)
			return
		}
		if stockRes.Code != 0 || stockRes.Data.Quote == nil {
			tctx.Logger.Errorf("获取雪球股票(%s)报价数据状态错误, <%s>\n%v", code, val)
			onError(r, errors.New(fmt.Sprintf("获取雪球股票(%s)报价错误，状态码：%d", code, stockRes.Code)))
			return
		}

		q := stockRes.Data.Quote
		q.Date = time.Time(q.Timestamp).Truncate(time.Hour * 24)
		onValue(r, q)
	}

	go func() {
		err := crawler.CrawlAndEval(cctx, url, request)
		if err != nil {
			cctx.Tracer().Logger.Errorf("chromedp异常，%v", err)
			onError(nil, err)
		}
	}()

	for code := range codes {
		request <- &StockQuoteCrawlRequest{
			Ctx:         tctx,
			Code:        code,
			OnValueFunc: onValueFunc,
			OnErrorFunc: onError,
		}
	}
}

// GetStockHistQuote 获取雪球股票历史报价
func GetStockHistQuote(tctx support.TracerCtx, codes <-chan string, onValue func(crawler.CrawlRequest, []*DailyQuoteHist), onError crawler.CrawlOnError) {
	cctx := crawler.NewCrawlContext(tctx)
	defer cctx.Cancel()
	url := "https://xueqiu.com/S/SH000001"
	request := make(chan crawler.CrawlRequest)
	defer close(request)
	onValueFunc := func(r crawler.CrawlRequest, val string) {
		stockRes := DailyQuoteHistResp{}
		code := comm.StockCode(r.(*DailyQuoteHistCrawlRequest).Code)
		if err := json.Unmarshal([]byte(val), &stockRes); err != nil {
			tctx.Logger.Errorf("解析雪球股票(%s)报价数据失败, <%s>\n%v", code, val, err)
			onError(r, err)
			return
		}
		if stockRes.Code != 0 {
			tctx.Logger.Errorf("获取雪球股票(%s)报价数据状态错误, <%s>\n%v", code, val)
			onError(r, errors.New(fmt.Sprintf("获取雪球股票(%s)报价错误，状态码：%d", code, stockRes.Code)))
			return
		}

		list := stockRes.Data.Item
		res := make([]*DailyQuoteHist, len(list), len(list))
		for i, e := range list {
			res[i] = &DailyQuoteHist{
				Code:          code,
				Date:          time.Unix(0, int64(e[0])*int64(time.Millisecond)),
				Volume:        int64(e[1]),
				Open:          e[2],
				High:          e[3],
				Low:           e[4],
				Close:         e[5],
				Change:        e[6],
				Percent:       e[7],
				TurnoverRate:  e[8],
				Amount:        e[9],
				PE:            e[12],
				PB:            e[13],
				PS:            e[14],
				PCF:           e[15],
				MarketCapital: e[16],
			}
		}
		onValue(r, res)
	}

	go func() {
		err := crawler.CrawlAndEval(cctx, url, request)
		if err != nil {
			cctx.Tracer().Logger.Errorf("chromedp异常，%v", err)
			onError(nil, err)
		}
	}()

	for code := range codes {
		request <- &DailyQuoteHistCrawlRequest{
			Ctx:         tctx,
			Code:        code,
			OnValueFunc: onValueFunc,
			OnErrorFunc: onError,
		}
	}
}
